package org.moonlight.jvm.classfile.constant;

import lombok.Getter;
import lombok.Setter;
import org.moonlight.jvm.classfile.ClassReader;
import org.moonlight.jvm.classfile.constant.info.ConstantClassInfo;
import org.moonlight.jvm.classfile.constant.info.ConstantNameAndTypeInfo;
import org.moonlight.jvm.classfile.constant.info.ConstantUtf8Info;

import java.util.HashMap;
import java.util.Map;

/**
 * class文件 中的 常量池. 由于还没有被加载到 JVM 中，所以称之为 “静态常量池”。静态常量池不仅仅包含字符串（数字）字面量，还包含类、方法的信息，
 * 占用class文件绝大部分空间。这种常量池主要用于存放两大类常量：字面量（Literal）和符号引用量（Symbolic References），
 * 字面量相当于Java语言层面常量的概念，如文本字符串，声明为final的常量值等。除了字面量，其他常量都是通过索引直接或间接指向 CONSTANT_Utf8_info 常量。
 *
 * 符号引用则属于编译原理方面的概念，包括了如下三种类型的常量：
 *   1. 类和接口的全限定名。
 *   2. 字段名称和描述符。
 *   3. 方法名称和描述符。
 *
 * 静态常量池在 class 文件中被组织成一个表，在手动解析静态常量池时，需要注意以下三点：
 *   1、表头给出的常量池大小比常量池的实际大小大 1，如表头的值为 n，实际大小就是 n-1。
 *   2、有效的常量池索引为 1~n-1，0 是无效索引。
 *   3、CONSTANT_Long_info 和 CONSTANT_Double_info 占用常量池的两个位置。
 *
 * @author Moonlight
 * @createTime 2023/8/15 8:55
 **/
public class StaticConstantPool {

    /** 常量池大小 **/
    @Getter
    private final int size;
    /** 常量信息数组 **/
    @Getter
    @Setter
    private ConstantInfo[] constantInfos;

    public StaticConstantPool(ClassReader reader) {
        this.size = reader.readUint16();
        this.constantInfos = new ConstantInfo[this.size];
        for (int i = 1, tag; i < this.size; i++) {
            this.constantInfos[i] = ConstantInfo.readConstantInfo(reader, this);
            // Long 和 Double 类型 需要占用两个位置
            // 常量池(不管静态还是动态)都还是需要给两个位置的，因为编译出来的class文件就是给的两个位置
            // 虽然在 操作数栈 和 局部变量表 这些地方，本工程并没有给 Long 和 Double 两个位置
            tag = constantInfos[i].tag();
            if (tag == ConstantInfo.CONSTANT_DOUBLE || tag == ConstantInfo.CONSTANT_LONG) {
                i++;
            }
        }
        System.out.println();
    }

    /**
     * 根据索引下标 从常量池 查找 字段或方法的 名字 和 描述符
     * @param idx 静态常量池索引下标
     * @return Map<String, String> 存储有 name type 两个KEY 分别代表 字段或方法的 名字 和 描述符
     * @createTime 14:38 2023/8/15
     * @author moonlight
     **/
    public Map<String, String> getNameAndType(int idx) {
        Map<String, String> map = new HashMap<>(8);
        ConstantNameAndTypeInfo nameAndTypeInfo = (ConstantNameAndTypeInfo) this.constantInfos[idx];
        map.put("name", this.getUtf8(nameAndTypeInfo.getNameIdx()));
        map.put("type", this.getUtf8(nameAndTypeInfo.getDescriptorIdx()));
        return map;
    }

    /**
     * 根据索引下标 从常量池 查找 类的类名
     * @param idx 静态常量池索引下标
     * @return String 类的类名
     * @createTime 14:43 2023/8/15
     * @author moonlight
     **/
    public String getClassName(int idx) {
        if (idx > 0) {
            ConstantClassInfo classInfo = (ConstantClassInfo) this.constantInfos[idx];
//        return this.getUtf8(classInfo.nameIdx);
            return classInfo.name();
        }
        return null;
    }

    /** 
     * 根据索引下标从常量池查找 MUTF8 编码的字符串
     *
     * @param idx 静态常量池索引下标
     * @return String 字符串
     * @createTime 14:25 2023/8/15
     * @author moonlight
     **/
    public String getUtf8(int idx) {
        ConstantUtf8Info utf8Info = (ConstantUtf8Info) this.constantInfos[idx];
        return utf8Info == null ? "" : utf8Info.getVal();
    }
}